package com.briup.common.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

/**
 * Redis 配置类
 */
@Configuration
public class RedisConfig {

    /**
     * 配置redis的序列化器，使用GenericJackson2JsonRedisSerializer序列化器；
     * 该序列化器可以将对象序列化为json字符串，并且可以将json字符串反序列化为对象；
     * <p>
     * Spring Data Redis 默认使用的序列化器是JdkSerializationRedisSerializer，
     * 该序列化器可以将对象序列化为二进制字节数组，并且可以将二进制字节数组反序列化为对象；
     * 但是，该序列化器的序列化后的字符串比较长，不便于阅读和存储；同时其序列化之后的数据与原有数据
     * 可能存在一定的差异，例如，当对象中包含了循环引用时，
     * JdkSerializationRedisSerializer序列化器会抛出异常，
     * 而GenericJackson2JsonRedisSerializer序列化器则可以正常序列化和反序列化；
     * 因此，我们可以使用GenericJackson2JsonRedisSerializer序列化器，
     * 该序列化器可以将对象序列化为json字符串，并且可以将json字符串反序列化为对象；
     * 同时，该序列化器的序列化后的字符串比较短，便于阅读和存储；
     */
    @Bean
    public RedisSerializer<Object> redisSerializer() {
        // 创建新的ObjectMapper实例，避免修改Spring默认的ObjectMapper
        ObjectMapper mapper = new ObjectMapper();

        // 添加JavaTimeModule以支持Java 8时间类型
        mapper.registerModule(new JavaTimeModule());

        // 禁用FAIL_ON_UNKNOWN_PROPERTIES属性，避免反序列化时因未知属性而失败
        mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // 配置ObjectMapper在序列化时包含类型信息
        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(),
                ObjectMapper.DefaultTyping.NON_FINAL);


        // 使用配置好的ObjectMapper创建GenericJackson2JsonRedisSerializer
        return new GenericJackson2JsonRedisSerializer(mapper);
    }

    /**
     * 配置redis的模板，使用RedisTemplate<String,Object>模板；
     * 该模板可以将字符串作为key，将对象作为value，进行存储和读取；
     * 同时，该模板可以使用GenericJackson2JsonRedisSerializer序列化器，
     * 对key和value进行序列化和反序列化；
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 创建RedisTemplate对象
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 设置连接工厂为redisConnectionFactory，
        // 该连接工厂可以连接到redis服务器，并且可以进行数据的读写操作；
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 设置key的序列化器为RedisSerializer.string()，
        // 该序列化器可以将字符串序列化为字节数组，并且可以将字节数组反序列化为字符串；
        redisTemplate.setKeySerializer(RedisSerializer.string());
        // 设置value的序列化器为redisSerializer()，
        // 该序列化器可以将对象序列化为json字符串，并且可以将json字符串反序列化为对象；
        redisTemplate.setValueSerializer(redisSerializer());
        // 设置hashKey的序列化器为RedisSerializer.string()，
        // 该序列化器可以将字符串序列化为字节数组，并且可以将字节数组反序列化为字符串；
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        // 设置hashValue的序列化器为redisSerializer()，
        // 该序列化器可以将对象序列化为json字符串，并且可以将json字符串反序列化为对象；
        redisTemplate.setHashValueSerializer(redisSerializer());

        return redisTemplate;
    }
}